package com.radar.visualize;

import cn.hutool.core.util.NumberUtil;
import com.radar.common.GisConstants;
import com.radar.common.ProductType;
import com.radar.io.cinrad.BaseCinrad;
import wcontour.Legend;
import wcontour.global.LPolygon;
import wcontour.global.LegendPara;
import wcontour.global.PointD;

import java.awt.*;
import java.awt.geom.GeneralPath;
import java.awt.geom.Path2D;
import java.awt.image.BufferedImage;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
 * @author: fy-wbj
 * @description: cinrad radar thematic map core lib
 * @date: 2020/9/22
 */
public class ThematicMap {

    BaseCinrad radar;

    public ThematicMap(BaseCinrad radar) {
        this.radar = radar;
    }

    //region color

    Color nullColor = new Color(0, 0, 0, 0);

    Color backColor = new Color(255, 255, 255, 30);

    static final double[] refValues = new double[]{0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 100};

    static final Color[] refColors = new Color[]{new Color(0, 0, 246), new Color(1, 160, 246), new Color(0, 236, 236), new Color(1, 255, 0),
            new Color(0, 200, 0), new Color(1, 144, 0), new Color(255, 255, 0), new Color(231, 192, 0), new Color(255, 144, 0),
            new Color(255, 0, 0), new Color(214, 0, 0), new Color(192, 0, 0), new Color(255, 0, 240), new Color(120, 0, 132), new Color(173, 144, 240)};

    static final double[] dorValues = new double[]{10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 100};
    static final Color[] dorColors = new Color[]{new Color(8, 161, 244), new Color(0, 236, 236), new Color(0, 216, 0), new Color(0, 144, 0), new Color(255, 255, 0), new Color(231, 192, 0),
            new Color(255, 144, 0), new Color(255, 0, 0), new Color(214, 0, 0), new Color(192, 0, 0), new Color(255, 0, 240), new Color(150, 0, 180), new Color(173, 144, 240)};

    static final double[] etValues = new double[]{0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 15, 17, 18, 20, 21, 23};
    static final Color[] etColors = new Color[]{new Color(0, 0, 0, 0), new Color(118, 118, 118), new Color(0, 224, 254), new Color(0, 176, 254), new Color(0, 144, 204), new Color(50, 0, 150),
            new Color(0, 250, 144), new Color(0, 186, 0), new Color(0, 238, 0), new Color(254, 190, 0), new Color(254, 254, 0), new Color(174, 0, 0),
            new Color(254, 0, 0), new Color(254, 254, 254), new Color(230, 0, 254)};

    static final double[] velValues = new double[]{-35, -27, -20, -15, -10, -5, -1, 1, 5, 10, 15, 20, 27, 28};
    static final Color[] velColors = new Color[]{new Color(102, 204, 255), new Color(0, 204, 255), new Color(0, 153, 153), new Color(0, 255, 0), new Color(0, 196, 0), new Color(0, 102, 0),
            new Color(255, 255, 255), new Color(255, 0, 0), new Color(255, 51, 51), new Color(255, 153, 153), new Color(255, 102, 0), new Color(255, 204, 0), new Color(255, 255, 0)};

    static final double[] vilValues = new double[]{0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70};
    static final Color[] vilColors = new Color[]{new Color(153, 153, 153), new Color(102, 102, 102), new Color(204, 153, 153), new Color(204, 102, 102), new Color(204, 102, 102), new Color(0, 204, 102),
            new Color(0, 151, 0), new Color(252, 252, 101), new Color(204, 204, 51), new Color(254, 51, 51), new Color(204, 0, 0), new Color(153, 0, 0), new Color(0, 0, 255), new Color(255, 255, 255)};

    static final double[] ohpValues = new double[]{1, 2.5, 5, 10, 25, 50, 100, 200};
    static final Color[] ohpColors = new Color[]{
            new Color(165,243,141),
            new Color(61,185,63),
            new Color(99,184,249),
            new Color(0,0,254),
            new Color(243,5,238),
            new Color(129, 0, 64),
            new Color(129, 0, 64)};


    public Color getColor(double[] values, Color[] colors, double value) {
        for (int i = 1; i < values.length; i++) {
            if (value >= values[i - 1] && value < values[i]) {
                return colors[i - 1];
            }
        }
        return null;
    }

    public Color getGradientColor(double[] values, Color[] colors, double value) {
        for (int i = 1; i < values.length; i++) {
            if (value >= values[i - 1] && value < values[i]) {
                double z = (value - values[i - 1]) / (values[i] - values[i - 1]);
                int r = (int) (colors[i - 1].getRed() + ((colors[i].getRed() - colors[i - 1].getRed()) * z));
                int g = (int) (colors[i - 1].getGreen() + ((colors[i].getGreen() - colors[i - 1].getGreen()) * z));
                int b = (int) (colors[i - 1].getBlue() + ((colors[i].getBlue() - colors[i - 1].getBlue()) * z));
                return new Color(r, g, b);
            }
        }
        return null;
    }

    public Color getColor(double v, ProductType type) {
        if(v == GisConstants.MASK){
            return new Color(255, 255, 255, 50);
        }
        if (type == ProductType.REF || type == ProductType.CR) {
            return getColor(refValues, refColors, v);
        } else if (type == ProductType.ET) {
            return getColor(etValues, etColors, v);
        } else if (type == ProductType.VEL) {
            return getColor(velValues, velColors, v);
        } else if (type == ProductType.VIL) {
            return getColor(vilValues, vilColors, v);
        }  else if (type == ProductType.OHP) {
            return getColor(ohpValues, ohpColors, v);
        } else if (type == ProductType.REFGRAY) {
            double z = v > 0 ? v / 80.0 * 255 : 0;
            z = z > 254 ? 255 : z;
            return new Color((int) z, (int) z, (int) z);
        }
        return null;
    }

    public double getValue(Color color, ProductType type) {
        Color[] c = null;
        double[] value = null;
        if (type == ProductType.REF || type == ProductType.CR) {
            c = refColors;
            value = refValues;
        } else if (type == ProductType.ET) {
            c = etColors;
            value = etValues;
        } else if (type == ProductType.VEL) {
            c = velColors;
            value = velValues;
        } else if (type == ProductType.VIL) {
            c = vilColors;
            value = vilValues;
        }
        for (int i = 0; i < c.length; i++) {
            if (color.getRGB() == c[i].getRGB()) {
                return (value[i + 1] - value[i]) / 2 + value[i];
            }
        }
        return GisConstants.UNDEF;
    }

    public Color getGradientColor(double v, ProductType type) {
        if (type == ProductType.REF || type == ProductType.CR) {
            return getGradientColor(refValues, refColors, v);
        } else if (type == ProductType.ET) {
            return getGradientColor(etValues, etColors, v);
        } else if (type == ProductType.VEL) {
            return getGradientColor(velValues, velColors, v);
        } else if (type == ProductType.VIL) {
            return getGradientColor(vilValues, vilColors, v);
        }
        return null;
    }

    //endregion

    //region common

    public BufferedImage drawSingleRaster(double[][] data, ProductType type) {
        return drawSingleRaster(data, type, Color.black);
    }

    public BufferedImage drawSingleRaster(double[][] data, Color backColor) {
        return drawSingleRaster(data, ProductType.REF, backColor);
    }

    public BufferedImage drawSingleRaster(double[][] data, ProductType type, Color backColor) {
        int width = data[0].length;
        int height = data.length;
        BufferedImage br = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = br.createGraphics();
        g.setBackground(backColor);
        g.clearRect(0, 0, width, height);
        g.setPaint(backColor);
        g.drawOval(0, 0, width, height);
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                Color color = getColor(data[i][j], type);
                if (color != null) {
                    br.setRGB(j, i, color.getRGB());
                }
            }
        }
        g.dispose();
        return br;
    }

    /**
     * Linear interpolation of latitude and longitude to picture coordinates
     *
     * @param pX     longitude
     * @param pY     latitude
     * @param minX
     * @param minY
     * @param scaleX
     * @param scaleY
     * @return
     */
    public int[] toScreen(double pX, double pY, double minX, double minY, double scaleX, double scaleY) {
        int sX = (int) ((pX - minX) * scaleX);
        int sY = (int) ((pY - minY) * scaleY);
        return new int[]{sX, sY};
    }

    /**
     * draw Vertical legend
     *
     * @param g         Graphics2D
     * @param type      radar type
     * @param startX    point0 start x
     * @param height    legend all height
     * @param width     legend item width
     * @param br        BufferedImage
     * @param fontColor line color
     */
    public void drawLegend(Graphics2D g, ProductType type, int startX, int height, int width, BufferedImage br, Color fontColor) {
        double[] cvalues = null;
        Color[] colors = null;
        if (type == ProductType.REF || type == ProductType.CR) {
            cvalues = refValues;
            colors = refColors;
        } else if (type == ProductType.ET) {
            cvalues = etValues;
            colors = etColors;
        } else if (type == ProductType.VEL) {
            cvalues = velValues;
            colors = velColors;
        } else if (type == ProductType.VIL) {
            cvalues = vilValues;
            colors = vilColors;
        }
        if (colors != null) {
            Color[] newColor = new Color[colors.length + 2];
            for (int i = 0; i < colors.length; i++) {
                newColor[i + 1] = colors[colors.length - 1 - i];
            }
            newColor[0] = nullColor;
            newColor[newColor.length - 1] = nullColor;
            colors = newColor;
            double[] newValues = new double[cvalues.length];
            for (int i = 0; i < newValues.length; i++) {
                newValues[i] = cvalues[newValues.length - 1 - i];
            }
            cvalues = newValues;
        } else {
            return;
        }
        LegendPara lPara = new LegendPara();
        PointD startPoint = new PointD();
        startPoint.X = startX;
        startPoint.Y = br.getHeight() - height - 10;
        lPara.startPoint = startPoint;
        lPara.isTriangle = true;
        lPara.isVertical = true;
        lPara.length = height;
        lPara.width = width;
        lPara.contourValues = cvalues;
        java.util.List<LPolygon> legendPolygons = Legend.createLegend(lPara);
        if (legendPolygons.size() > 0) {
            LPolygon aLPolygon;
            int i, j;
            java.util.List<Double> values = new ArrayList<>();
            for (double v : cvalues) {
                values.add(v);
            }
            PointD aPoint;
            for (i = 0; i < legendPolygons.size(); i++) {
                aLPolygon = legendPolygons.get(i);
                double aValue = aLPolygon.value;
                int idx = values.indexOf(aValue) + 1;
                Color aColor;
                if (aLPolygon.isFirst) {
                    aColor = colors[0];
                } else {
                    aColor = colors[idx];
                }
                List<PointD> newPList = aLPolygon.pointList;
                int len = newPList.size();
                GeneralPath drawPolygon = new GeneralPath(Path2D.WIND_EVEN_ODD, len);
                int dx = 0, dy = 0;
                for (j = 0; j < len; j++) {
                    aPoint = newPList.get(j);
                    int[] sxy = new int[]{(int) aPoint.X, (int) aPoint.Y};
                    if (j == 0) {
                        drawPolygon.moveTo(sxy[0], sxy[1]);
                    } else {
                        drawPolygon.lineTo(sxy[0], sxy[1]);
                        if (j == 2) {
                            dx = sxy[0];
                            dy = sxy[1];
                        }
                    }
                }
                drawPolygon.closePath();
                g.setColor(aColor);
                g.fill(drawPolygon);
                g.setColor(fontColor);
                g.draw(drawPolygon);
                if (i < legendPolygons.size() - 1) {
                    String str = String.valueOf((int) cvalues[i]);
                    BigDecimal b = new BigDecimal(cvalues[i]);
                    if (NumberUtil.isDouble(b.toString())) {
                        str = String.valueOf(b.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue());
                    }
                    g.setColor(fontColor);
                    if (i == 0 || i == legendPolygons.size() - 1) {
                        g.drawString(str, dx + width + 10, dy);
                    } else {
                        g.drawString(str, dx + 10, dy);
                    }
                }
            }
        }
    }

    public String simpleDoubleText(double x) {
        String str = String.valueOf((int) x);
        BigDecimal b = new BigDecimal(x);
        if (NumberUtil.isDouble(b.toString())) {
            str = String.valueOf(b.setScale(3, BigDecimal.ROUND_HALF_UP).doubleValue());
        }
        return str;
    }

    //endregion
}
